【AWS】はじめてのCloudSearch/ElasticMapReduceでCSV形式に変換したログをCloudSearchで検索する
はじめに
こんにちは植木和樹です。今回はCloudSearchを使ったログの検索を試してみたいと思います。
前回のエントリ「はじめてのEMR/fluentdでS3にアップロードしたログをElastic MapReduceで検索・集計する」では、S3に置いたログファイルをElastic MapReduceを使って直接検索しました。ただこの方法だと、検索するたびに毎回重量級サービスのEMRを起動しなければなりませんし、なによりS3のログファイルを直接扱うため遅いです。
そこで方法を少し見直して、EMRでfluentdのログファイルをCSV形式に変換した後、そのデータをCloudSearchに与えることでインデックス化し全文検索ができるようにしてみたいと思います。
CloudSearchってなに?
CloudSearchを一言でいえば「Googleのような全文検索システムを提供してくれるサービス」です。
ドキュメント情報をSDF(Search Data Format)という形式にしてCloudSearchにアップロードすると、そこからインデックスを作成してくれます。「Search Endpoint」というエンドポイント(URL)が提供されるので、そのURLにhttp://search-example-xxxxxxxx.ap-southeast-1.cloudsearch.amazonaws.com?q=foobarというようにクエリーを投げるとfoobarにマッチするドキュメント情報をJSONやXML形式で返してくれます。
この返されたJSON(XML)をパースして検索結果ページを表示することで、サイト独自の検索サービスを提供することができます。CloudSearchはインデックス管理と検索窓口を提供してくれるサービスと考えていいでしょう。
準備するもの
- 前回のエントリで「archivelog_201308」にてS3に出力したCSVファイル
- CloudSearchを操作するためのコンソール(Amazon Linux EC2を使用しました)
CloudSearchセットアップ
CloudSearchを使うためにはまず「検索ドメイン」を作成します。検索ドメインごとにエンドポイントが提供され、インデックスの管理が行われます。またエンドポイントへのアクセス制御や、検索結果ランキングに影響する項目の「重み付け(Rank Expressions)」などもドメイン毎に管理されています。
2013年9月12日現在 TokyoリージョンではCloudSearchは提供されていません。そのためシンガポール(ap-southeast-1)リージョンを使用しています。
マネージメントコンソールからCloudSearchの画面を開き[Create Your First Search Domain]をクリックします。
Search Domain Nameを入力します。今回はclassmethodドメインにしておきます。
インデックスのフィールドを決めます。サンプルがあれば、そこから自動判定することができるのですが(EMRで処理したため)1行目にヘッダー情報のないCSVの場合は自動判定に失敗するようです。そのためここでは "Manual Configuration" を選び、後ほど設定することにします。
アクセス制御を設定します。CloudSearchでは「検索用エンドポイント」と「ドキュメント操作用エンドポイント」の2種類のサービスが提供されています。それぞれ毎にどのIPアドレスからのアクセスを許可するかを設定することができます。今回は "Allow everyone access to all services" をクリックし、どこからでもアクセスできるようにしておきます。
確認画面が表示されますので[Confirm]をクリックします。
ドメインの作成処理が開始されます。
ドメインが作成されました。ただし初期化には30分近くかかります(!)。
CloudSearchの画面に戻り、作成したドメインのステータスが「LOADING」から「ACTIVE」になれば初期化完了です。
次にCSVファイルからSDF(Search Data Format)ファイルを作成して、ドメインにドキュメントをアップロードしてみましょう。
CloudSearch API Toolsをインストール
まずはコンソールとなるEC2にコマンドラインツールをインストールします。CloudSearchのapi-toolsはRPMでは提供されていないようです。ネットからアーカイブをダウンロードしてec2-userのホームディレクトリに展開します。
$ curl -O https://s3.amazonaws.com/amazon-cloudsearch-data/cloud-search-tools-1.0.2.3-2013.08.02.tar.gz $ tar xzf cloud-search-tools-1.0.2.3-2013.08.02.tar.gz
次に環境変数を設定します。なんどもログインし直すなら~/.bash_profileに追記しておきましょう。また上で説明した通りシンガポールリージョンを使っているのでCS_ENDPOINTはap-southeast-1になります。IAM-Roleに対応していないので、CloudSearchをフルコントロールできるIAMユーザーを作成し、アクセスキーとシークレットアクセスキーを設定します。
$ export CS_HOME=~/cloud-search-tools-1.0.2.3-2013.08.02 $ export PATH=$CS_HOME/bin:$PATH $ export CS_ENDPOINT=cloudsearch.ap-southeast-1.amazonaws.com $ export AWS_CREDENTIAL_FILE=~/cred.txt $ cat ~/cred.txt AWSAccessKeyId=<ACCESS_KEY> AWSSecretKey=<SECRET_KEY>
SDFファイルの作成とCloudSearchへのアップロード
CloudSearch api-toolsに含まれている cs-generate-sdf コマンドを使うとCSVファイルをSDFファイルに変換してくれます。ただEMRで作成したCSVファイルは1行目にヘッダー文字列がないため、CSV形式として自動判定してくれません。また拡張子がcsvでないとCSVと判断してくれないようです。そこでS3からCSVファイルをダウンロードしてヘッダー行と拡張子をつけてから処理します。ファイルのダウンロードには先日正式リリースされたAWS CLIを使ってみましょう。
$ mkdir csv $ aws s3 sync --region ap-northeast-1 s3://cm-fluentd-emr/archives/2013/08/ csv download: s3://cm-fluentd-emr/archives/2013/08/7662e24f-e26d-4526-ad07-37b87640c75c_000000 to csv/7662e24f-e26d-4526-ad07-37b87640c75c_000000 $ for file in `ls csv/*`; do echo $file; echo "version,dt,host,user,method,path,code,size,referer,agent" > $file.csv cat $file >> $file.csv rm $file done $ ls csv/ 7662e24f-e26d-4526-ad07-37b87640c75c_000000.csv
それではcs-generate-sdfコマンドでSDFファイルを作成しましょう。
$ mkdir sdf $ cs-generate-sdf --source ./csv/*.csv --output ./sdf --exclude-metadata Processing /home/ec2-user/./csv/7662e24f-e26d-4526-ad07-37b87640c75c_000000.csv (Using field 'version' as version というメッセージが続く) $ ls sdf/ 1.sdf $ head -20 sdf/1.sdf
SDFはJSON形式になっています。
[ { "type" : "add", "id" : "d__home_ec2_user___csv_7662e24f_e26d_4526_ad07_37b87640c75c_000000_csv_1", "version" : 1378951384, "lang" : "it", "fields" : { "dt" : "2013-08-28T07:16:41+00:00", "host" : "59.xxx.xxx.xxx", "path" : "/", "method" : "GET", "referer" : "-", "code" : "403", "agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36", "user" : "-", "size" : "3839" } }, { "type" : "add", "id" : "d__home_ec2_user___csv_7662e24f_e26d_4526_ad07_37b87640c75c_000000_csv_2", "version" : 1378951385,
JSON形式のファイル1.sdfが作成されました。fieldsにApacheログの各フィールドが含まれていることが分かります。
さてこのSDFファイルをclassmethodドメインにアップロードすることでインデックスを作成したいのですが、ドメイン作成時にどのフィールドをインデックスにするかの指定をスキップしていました。そこでcs-configure-from-sdfコマンドを使ってインデックス情報を適当に設定してみましょう。
$ cs-configure-from-sdf --domain-name classmethod --source ./sdf/1.sdf Connecting to CloudSearch in region [ap-southeast-1] Detected source format for ./sdf/1.sdf:json Analyzing ./sdf/1.sdf ----------------------------------------------------------------------------------- Existing field configuration for the domain - classmethod : ----------------------------------------------------------------------------------- Detected field configurations from all the sources : dt text (Result) host literal (Search Facet) path text (Result) method literal (Search Facet) referer text (Result) code uint () agent literal (Search Facet) user literal (Search Facet) size text (Result) ----------------------------------------------------------------------------------- New proposed field configuration for the domain - classmethod : dt text (Result) [NEW] host literal (Search Facet) [NEW] path text (Result) [NEW] method literal (Search Facet) [NEW] code uint () [NEW] referer text (Result) [NEW] user literal (Search Facet) [NEW] agent literal (Search Facet) [NEW] size text (Result) [NEW] ----------------------------------------------------------------------------------- Configure [classmethod] with analyzed fields y/N: y Configuring fields Defined 9 field configurations.
各フィールドをどのように扱うかが右端のカッコで示されています。
- Search ... 検索対象となるフィールド
- Result ... 検索結果に表示するフィールド
- Facet ... 検索結果をさらに絞り込み検索する際のフィールド
インデックスのタイプには3種類があり、それぞれ特徴があります。
タイプ | 設定値 | Search | Facet | Result | 備考 |
---|---|---|---|---|---|
text | 文字列 | 常に対象 | △ | ○ | 検索フィールドを選択しなかった場合に常に検索対象になる ResultまたはFacetどちらかに指定できる |
literal | 完全一致文字列 | ○ | ○ | △ | FacetまたResultどちらかに指定できる |
uint | 正の整数値 | 常に対象 | 常に対象 | 常に対象 | 重み付け(Rank Expressions)に使用できる |
詳しくはドキュメントを参照してください(Amazon CloudSearch Developer Guide)。上記の例だとhostmethoduseragentも検索フィールドに含め、dtpathreferersizeが検索結果に表示されます。(Agentはliteralでなくtextが適当だと思うのですが・・・)
それではSDFをアップロードしてインデックスの作成を行いましょう。アップロードにはcs-post-sdfコマンドを用います。またアップロードしただけではインデックス処理が実行されませんのでcs-index-documentsコマンドを使ってインデックス処理をキックしてあげましょう。
$ cs-post-sdf --domain-name classmethod --source ./sdf/1.sdf Connecting to CloudSearch in region [ap-southeast-1] Processing: ./sdf/1.sdf Detected source format for ./sdf/1.sdf as json Status: success Added: 13 Deleted: 0 $ cs-index-documents --domain-name classmethod
インデックス化処理には非常に時間がかかります(20分以上)。マネージメントコンソールでドメインのステータスが"PROCESSING"から"ACTIVE"になるのを待ちましょう。
検索ドメインのステータスが"ACTIVE"になったら準備完了です。適当な文字列で検索してみましょう。ここでは検索フィールドを指定していないので、すべてのtextフィールドが検索対象になります。
pathに"aaaa"が含まれているログ行がマッチしたため検索結果に表示されています。
おまけ
cs-generate-sdf --helpで出力される--sourceオプションの使い方には「 Accepts Apache-ant style wildcards such as */** for files and S3 prefixes.」とあるので、当初S3のとあるフォルダ以下のファイルすべてを対象にする際に、次のように指定したのですがドキュメントがマッチしませんでした。
$ cs-generate-sdf --source s3://cm-fluentd-test/2013/*/** --output ./output
*/**の構文はローカルディレクトリのファイルを指定するときのみ使えるようです。S3の場合は単純に上位フォルダを指定すれば、下位フォルダのファイルはすべて対象になります。
$ cs-generate-sdf --source s3://cm-fluentd-test/2013/ --output ./output
まとめ
今回はCloudSearchを使ってログファイルの全文検索を行ってみました。最初はインデックスタイプやSDFのフォーマットが分からずとまどいましたが、コツをつかむと高速で柔軟な検索が作れて楽しいです。
しかしSDFを作るためには一旦ログファイルをローカルに保存して、いくつかの前処理をする必要があります。ログファイルが大量になると、ファイルをバッチ処理するところで一工夫が必要になりそうです。
今回のCloudSearchで取り上げなかった「ドキュメントのバージョニング」や「検索フィールドの重み付け」「日本語検索」をもう少し詰めれば、日本語サイトでも独自の検索システムが実用的かつ手頃に構築できそうです。特にバージョニングについてはバッチ処理でインデックスを更新する上で避けられない部分なので、引き続き調査を行いたいと思います。